using System;
using System.Threading;
using WebSocket4Net;
using ErrorEventArgs = SuperSocket.ClientEngine.ErrorEventArgs;
using WebSocketState = WebSocket4Net.WebSocketState;

namespace FixedHeaderSample.WebSocketClient
{
    public class WebSocket4NetHelper
    {
        public event Action Opened = () => { };
        public event Action<string> Closed = (reason) => { };
        public event Action<Exception> Error = (exception) => { };
        public event Action<byte[]> DataReceived = (data) => { };
        public event Action<string> MessageReceived = (data) => { };

        #region IsWorking

        private bool _working;
        private readonly object _workingLockHelper = new object();

        private bool IsWorking
        {
            get
            {
                bool ret;
                lock (_workingLockHelper)
                {
                    ret = _working;
                }

                return ret;
            }
            set
            {
                lock (_workingLockHelper)
                {
                    _working = value;
                }
            }
        }

        #endregion

        private readonly WebSocket4Net.WebSocket _webSocket;

        public WebSocket4NetHelper(string url)
        {
            _webSocket = new WebSocket4Net.WebSocket(url);
            _webSocket.Opened += OnWebSocketOpened;
            _webSocket.Closed += OnWebSocketClosed;
            _webSocket.Error += OnWebSocketError;
            _webSocket.MessageReceived += OnMessageReceived;
            _webSocket.DataReceived += OnDataReceived;
        }

        public void Start()
        {
            IsWorking = true;
            Connect();
        }

        public void Stop()
        {
            IsWorking = false;
            if (_webSocket.State == WebSocketState.Open)
            {
                _webSocket.CloseAsync().GetAwaiter();
            }
        }

        public void Send(byte[] data)
        {
            if (data == null || data.Length == 0)
            {
                return;
            }

            if (_webSocket.State == WebSocketState.Open)
            {
                _webSocket.Send(data, 0, data.Length);
            }
        }

        public void Send(string data)
        {
            if (string.IsNullOrWhiteSpace(data))
            {
                return;
            }

            if (_webSocket.State == WebSocketState.Open)
            {
                _webSocket.Send(data);
            }
        }

        #region WebSocket Event Handler

        private void OnWebSocketOpened(object sender, EventArgs e)
        {
            Opened?.Invoke();
        }

        private void OnWebSocketClosed(object sender, EventArgs e)
        {
            Closed?.Invoke(e.ToString());
            Thread.Sleep(1);
            Connect();
        }

        private void OnWebSocketError(object sender, ErrorEventArgs e)
        {
            Error?.Invoke(e.Exception);
            Thread.Sleep(1);
            Connect();
        }

        private void OnMessageReceived(object sender, MessageReceivedEventArgs e)
        {
            MessageReceived?.Invoke(e.Message);
        }

        private void OnDataReceived(object sender, DataReceivedEventArgs e)
        {
            DataReceived?.Invoke(e.Data);
        }

        private void Connect()
        {
            if (IsWorking)
            {
                _webSocket.OpenAsync().GetAwaiter();
            }
        }

        #endregion
    }
}
